贪吃蛇案例
这个主题实际上是我很早之前写的一个案例教程,为了凑数也放到我的教程博客中吧。这个教程主要涉及一些俄罗斯方块类的矩阵游戏的基本思路和算法。
目的
贪吃蛇是一个很好的游戏教学项目,其原因在于:
• 基本的实物抽象思想
• 基本的键盘按下回调
• 基本的图形显示
• 基本的网格概念
• 基本的表数据更新
• 几乎无限的可拓展
思路
我们先整理下贪吃蛇游戏的组成和游戏规则,然后我们初步抽象成逻辑关系,然后进一步抽象为伪代码,最后按照lua的语法形成执行代码。至于后续的美工,优化不作为本节重点。
贪吃蛇是一个方块阵列游戏。游戏内容包括地图,边界,蛇,食物。规则包括蛇每次蛇按某方向移动一次,如果吃到食物则蛇长增长一格,如果碰壁或自己则游戏结束。
我们来分别分析一下。
地图 是由固定数量的方块构成的矩阵组成,每个方块有两种状态,黑和白。因此,考虑建立一个二维数组来存储每个方块的状态,同时,方块的阵列位置和显示位置也构成简单的函数关系。可以让周围一圈为黑作为墙。
蛇是一组连续的方块,从蛇的特性,类似火车,每次往前走,蛇头向控制方向移动一格,蛇头的后一节成为蛇头的位置,以此类推。那么,我们想到了,蛇的本质是一个存储着矩阵位置的数组,蛇头是第一个元素,而其他蛇节构成后面的,每个元素存储着(x,y),蛇头是有一个方向来控制,可能是上下左右中的一个,上的概念在矩阵里实际上是(0,-1),其他方向类推。在每次移动时,蛇头按方向变更其位置,而蛇节则继承其前一节的位置。而在地图上所有蛇的位置为黑。
食物,实际上就是矩阵中的一个方块。当蛇吃到食物,也就是蛇头的位置与食物相同时,蛇节的所代表的数组总长度加1,里面存储蛇尾的位置。食物在地图上为黑。
碰撞判断,如何判断蛇碰壁或碰自己呢?很简单,只要判断地图是否为黑就行了!哦,别忘了食物也是黑的,所以如果蛇头下一个位置如果为黑,那么不是碰壁就是吃到食物了,食物数量很少,分别判断就行了。
游戏速度,显示帧率为60hz,我们的蛇每秒仅需走几次,根据游戏速度不同,那么我们需要差速更新逻辑。
输入输出,输入我们仅仅需要上下左右就行了,我们通过输入来改变蛇的方向,但是蛇不能够向回转哦!输出我们暂时只用最基本的画矩阵的两个状态,白色就是线框方形,黑色就是填充方形。
核心循环,上面是所有我们已经有的信息,现在让我们把它们串起来来完成我们的核心。首先,我们要读取按键信息来改变蛇的方向,然后我们的蛇按照方向移动,移动后就肯定有我们的碰撞判断。碰撞判断告诉我们蛇是普通,吃食物还是挂了,然后继续我们的循环。如果吃了食物就要在空位置再生成一个,挂了就要弹出gameover!了。
设计
好像我们已经把贪吃蛇的轮廓刻画出来了,我们来用流程图和伪代码来实战演习一下!
初始化
定义地图为二维数组,每个位置初始为假
定义蛇为一个数组,初始为长度为5的蛇,放在矩阵中间。
###循环
读取输入改变方向
蛇按方向移动
碰撞判断~否,正常移动:是,如果是食物则增长;否则游戏结束。
绘图
画矩阵
代码
好啦,程序架构已经完成啦,接下来我们用lua代码把上面的流程翻译一下,就是我们的贪吃蛇啦。
注意,我们这里的代码并不是最简单和优化最好的,但是相比来讲更加适合理解。
首先,我们把地图矩阵做出来,同时为了有可视化效果,我们再把绘图做出来。
|
|
然后,我们定义一条蛇,放在屏幕中间。暂时它还是条死蛇^_^。
|
|
这里 我们要插入一个差速更新的方法,使每隔固定时间长度,蛇更新一次。
|
|
想要控制方向我们需要读取按键或者按下回调。注意,蛇不能后退哦!
我们之所以用一个dir来控制方向是因为,蛇的方向更新的响应并不是立即的,也就是并不是按下左蛇就要直接向左移动一格,而是在蛇差速更新时,才会按方向移动,因此需要用一个变量来保存这个方向。
|
|
local function eat()
table.insert(snake, {x=snake[1].x,y=snake[1].y}) –实际上 增加一节的位置可以是蛇上的任意点,因为下一帧会跳到上一次倒数第二的位置。
updateCD=updateCD*0.9 –cd速度打九折
newFood() –生成一个新的食物
end
|
|
local targetX,targetY=snake[1].x+dir.x,snake[1].y+dir.y –实际上检验的位置是移动后的蛇头
if check(targetX,targetY) then
if targetX==food.x and targetY==food.y then –如果位置是食物就吃了它,并生成新的食物
eat()
else –如果位置是墙就gameover了,这里注意有个return 因为游戏结束了,后面不需要再更新了。
gameover()
return
end
end
|
|